package com.example.tofcamera;

import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.hardware.Camera;
import android.os.Environment;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Space;
import android.widget.TextView;

import com.vm.lib.NativeWarpper;

import org.w3c.dom.Text;

import java.io.File;
import java.io.IOException;

import static java.lang.Math.max;
import static java.lang.Math.min;
import static java.lang.Math.round;

public class MainActivity extends AppCompatActivity implements Camera.PreviewCallback {

    SurfaceView surfaceview;
    SurfaceHolder surfaceholder;
    Camera camera = null;

    private Surface mSurface;
//    int width=2064;    int height=1548;
    int width=224;    int height=172;
    int total=width*height;

    protected FrameQueue frameQueue=null;

    protected DisplayFrameThread dft=null;

    EditText edt=null;
    EditText edt2=null;

    Button btn=null;

    public TextView tv = null;
    public TextView tv2 = null;
    public TextView tv3 = null;

    //Result layot
    public LinearLayout measureLayout;

    public LinearLayout calibrateLayout;
    public LinearLayout distanceLayout;
    public Space space;
    public TextView frameRecordedTv;
    public TextView width_tv, height_tv, length_tv;

    public TextView distance_tv;

    String TAG = "MainActivity";

    public double fx_tof = 217.869247;
    public double fy_tof = 217.869247;
    public double cx_tof = 110.485161;
    public double cy_tof = 84.037331;
    public double k1_tof = -0.136084;
    public double k2_tof = -1.520149;
    public double k3_tof = -0.000889;
    public double p1_tof = 0.000396;
    public double p2_tof = 3.170387;

    public float[] metric = new float[]{0, 0, 0};
    public float[] plane = new float[]{0, 0, -1, 1000};
    public float[] K = new float[]{(float) fx_tof, 0, (float) cx_tof, 0, (float) fy_tof, (float) cy_tof, 0, 0, 1};
    public float[] D = new float[]{(float) k1_tof, (float) k2_tof, (float) k3_tof, (float) p1_tof, (float) p2_tof};
    public byte[] mask = new byte[width * height];

    public int[] vertex = null;
    public int mode = 3;
    public String[] mstr=new String[]{"Object Measurement", "Calibrate", "Capture", "Distance Measurement"};

    int frameIdTOF = 0;

    float heightCut=10;
    int pointNumberThreshold=50;

    public void SetRoi(String strRoi)
    {
        String[] splitStr = strRoi.split("\\s+");
        vertex=new int[splitStr.length];
        for(int i=0;i<vertex.length;i++)
            vertex[i]=Integer.parseInt(splitStr[i]);

        DisplayFrameThread.vertex=new int[vertex.length];
        System.arraycopy(vertex, 0, DisplayFrameThread.vertex, 0, vertex.length);

        NativeWarpper.GetPolygonMask(width, height, vertex, mask);
    }

    public void SetParam(String strParam)
    {
        String[] splitStr = strParam.split("\\s+");
        if(splitStr.length>0)
            heightCut=Float.parseFloat(splitStr[0]);
        if(splitStr.length>1)
            pointNumberThreshold=Integer.parseInt(splitStr[1]);
    }


    public void writeSDFile(String fileName, byte[] bytes) throws IOException {

        String f = Environment.getExternalStorageDirectory().getPath();//获取SD卡目录
        String sDir = "thetaj";
        File destDir = new File(f + "/" + sDir);
        if (!destDir.exists()) {
            destDir.mkdirs();
        }

//        File file = new File(f+"/"+sDir+"/"+fileName);
//        FileOutputStream fos = new FileOutputStream(file);
//        fos.write(bytes);
////        fos.writeBytes()
//        fos.close();

        NativeWarpper.DecodeAndSave(bytes, f + "/" + sDir + "/" + fileName);
    }


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

        ActionBar actionBar = getSupportActionBar();
        actionBar.hide();

        surfaceview = (SurfaceView) findViewById(R.id.grayview);

        surfaceholder = surfaceview.getHolder();
        surfaceholder.addCallback(new surfaceholderCallbackBack());

        SurfaceView sv = (SurfaceView) findViewById(R.id.surfaceview);
        sv.getHolder().addCallback(new surfaceholderCallbackTOFgray());

        edt=(EditText)findViewById(R.id.editText);
        edt2=(EditText)findViewById(R.id.editText2);
        btn=(Button)findViewById(R.id.button);

        btn.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                SetRoi(edt.getText().toString());
                SetParam(edt2.getText().toString());
            }
        });

//        tv = (TextView) findViewById(R.id.textView);
//        tv.setText("Not Ready");
        tv2 = (TextView) findViewById(R.id.textView2);
        tv2.setText("Not Ready");

        tv3 = (TextView) findViewById(R.id.mode_tv);
        tv3.setText(mstr[mode]);

        SetRoi(edt.getText().toString());
        SetParam(edt2.getText().toString());

        measureLayout = (LinearLayout) findViewById(R.id.measure_layout);
        calibrateLayout = (LinearLayout) findViewById(R.id.calibrate_layout);

        space = (Space) findViewById(R.id.space);
        frameRecordedTv = (TextView) findViewById(R.id.frame_recorded_tv);
        //Hide layout
        calibrateLayout.setVisibility(LinearLayout.GONE);
        measureLayout.setVisibility(LinearLayout.GONE);

        width_tv = (TextView) findViewById(R.id.width_tv);
        height_tv = (TextView) findViewById(R.id.height_tv);
        length_tv = (TextView) findViewById(R.id.length_tv);

        distance_tv = (TextView) findViewById(R.id.textView4);
        distanceLayout = (LinearLayout) findViewById(R.id.distance_layout);
        distanceLayout.setVisibility(LinearLayout.GONE);
    }

    @Override
    public void onPreviewFrame(byte[] data, Camera camera) {

        Log.d(TAG, "onPreviewFrame"+"data="+data.length);

        if(frameQueue!=null)
        {
            byte [] graybuf=new byte[total];
            System.arraycopy(data, total*2, graybuf, 0, total);

            try {
                frameQueue.put(new Frame(graybuf));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }


        frameIdTOF++;
        if (mode == 0) {

            LinearLayout.LayoutParams param = new LinearLayout.LayoutParams(
                    LinearLayout.LayoutParams.MATCH_PARENT,
                    0,
                    0.0f
            );
            space.setLayoutParams(param);
            calibrateLayout.setVisibility(LinearLayout.GONE);
            measureLayout.setVisibility(LinearLayout.VISIBLE);
            distanceLayout.setVisibility(LinearLayout.GONE);
            NativeWarpper.MeasureObject(metric, data, plane, K, D, mask, width, height, heightCut, pointNumberThreshold);
            float width = min(metric[0], metric[1]);
            float length = max(metric[0], metric[1]);
            width_tv.setText("" +  round(width));
            length_tv.setText("" + round(length));
            height_tv.setText("" +  round(metric[2]));
//            String msg = "width(mm):" + round(width) + " length(mm): " + round(length) + " height(mm): " + round(metric[2]);
//            tv.setText(msg);
        }

        if (mode == 1) {
            LinearLayout.LayoutParams param = new LinearLayout.LayoutParams(
                    LinearLayout.LayoutParams.MATCH_PARENT,
                    0,
                    1.0f
            );
            space.setLayoutParams(param);
            calibrateLayout.setVisibility(LinearLayout.VISIBLE);
            measureLayout.setVisibility(LinearLayout.GONE);
            distanceLayout.setVisibility(LinearLayout.GONE);
            NativeWarpper.CalibratePlane(plane, data, K, D, mask, width, height);
            String msg = plane[0] + ", " + plane[1] + ", " + plane[2] + ", " + plane[3] + ", ";
            tv2.setText(msg);
        }

        if (mode == 2) {
            LinearLayout.LayoutParams param = new LinearLayout.LayoutParams(
                    LinearLayout.LayoutParams.MATCH_PARENT,
                    0,
                    2.0f
            );
            space.setLayoutParams(param);
            calibrateLayout.setVisibility(LinearLayout.GONE);
            measureLayout.setVisibility(LinearLayout.GONE);
            distanceLayout.setVisibility(LinearLayout.GONE);
            try {
                writeSDFile(frameIdTOF + "", data);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        if (mode == 3) {
            LinearLayout.LayoutParams param = new LinearLayout.LayoutParams(
                    LinearLayout.LayoutParams.MATCH_PARENT,
                    0,
                    1.0f
            );
            space.setLayoutParams(param);
            calibrateLayout.setVisibility(LinearLayout.GONE);
            measureLayout.setVisibility(LinearLayout.GONE);
            distanceLayout.setVisibility(LinearLayout.VISIBLE);

            float avgDistance=NativeWarpper.MeasureDistance(data, K, D, mask, width, height);

            if(avgDistance<0)
                distance_tv.setText("Out of Range");
            else
                distance_tv.setText("" +  round(avgDistance));

        }

        tv3.setText(mstr[mode]);
        frameRecordedTv.setText("" + frameIdTOF);

    }


    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {

        switch (keyCode)
        {
            case KeyEvent.KEYCODE_VOLUME_DOWN:
                mode=(mode+3)%4;
                tv3.setText(mstr[mode]);
                frameRecordedTv.setText("" + frameIdTOF);

                return true;
            case KeyEvent.KEYCODE_VOLUME_UP:
                mode=(mode+1)%4;
                tv3.setText(mstr[mode]);
                frameRecordedTv.setText("" + frameIdTOF);
                return true;
        }


        return super.onKeyDown(keyCode, event);
    }

    class surfaceholderCallbackBack implements SurfaceHolder.Callback {
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            // 获取camera对象
            int cameraCount = Camera.getNumberOfCameras();
            if (cameraCount > 0) {
                camera = Camera.open(0);
                // 设置预览监听
                try {
                    camera.setPreviewDisplay(holder);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                Camera.Parameters parameters = camera.getParameters();
//
                if (MainActivity.this.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) {
                    parameters.set("orientation", "portrait");
                    camera.setDisplayOrientation(90);
                    parameters.setRotation(90);
                } else {
                    parameters.set("orientation", "landscape");
                    camera.setDisplayOrientation(0);
                    parameters.setRotation(0);
                }
                camera.setParameters(parameters);
                // 启动摄像头预览
                camera.setPreviewCallback(MainActivity.this);
                camera.startPreview();
                System.out.println("camera.startpreview");

            }
        }

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            camera.setPreviewCallback(null);
            camera.stopPreview();
            camera.release();
        }
    }


    class surfaceholderCallbackTOFgray implements SurfaceHolder.Callback {

        @Override
        public void surfaceCreated(SurfaceHolder holder) {

            Log.d(TAG, "surfaceCreated0");
            mSurface = holder.getSurface();

            frameQueue=new FrameQueue(2);
            frameQueue.setFrameFormat(width,height,1);


            dft=new DisplayFrameThread(mSurface, frameQueue);

            dft.start();

            Log.d(TAG, "surfaceCreated1");

        }

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {

            try {
                frameQueue.put(new Frame(null));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            if(dft!=null) {
                try {
                    dft.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
